home *** CD-ROM | disk | FTP | other *** search
/ Sounds Terrific 2 / Sounds Terrific II (1996)(Weird Science)(Disc 1 of 2)[Amiga-PC].iso / archives / amiga / audio_handler.lha / audio.c < prev    next >
C/C++ Source or Header  |  1995-08-01  |  23KB  |  785 lines

  1. /*
  2.  * Audio Handler
  3.  *
  4.  * Copyright (c) 1992,1995 by Martin Brenner, martin@ego.oche.de
  5.  *
  6.  * THIS INFORMATION IS PROVIDED "AS IS"; NO WARRANTIES ARE MADE.
  7.  * ALL USE IS AT YOUR OWN RISK, AND NO LIABILITY OR 
  8.  * RESPONSIBILITY IS ASSUMED.
  9.  *
  10.  * This handler is partially based on port-handler by Andy Finkel.
  11.  * Some insight about DOS and the SAS C Compiler was gained from
  12.  * the Guru Book by Ralph Babel.
  13.  *
  14.  * For each new invocation of this handler a new process is started,
  15.  * just like the console handler. Options after the colon ':' are
  16.  * evaluated and Audio output is done with double buffering.
  17.  *
  18.  * This is an example for a handler where incoming DOS packets are
  19.  * NOT the same length as messages sent to the exec device. Instead,
  20.  * data is buffered and sent to the device au bloc.
  21.  *
  22.  * $Header: Work:Progs/aud/RCS/audio.c,v 1.4 1995/08/01 03:04:48 martin Exp martin $
  23.  *
  24.  * $Log: audio.c,v $
  25.  * Revision 1.4  1995/08/01  03:04:48  martin
  26.  * Bug fixes for version 1.0 beta
  27.  *
  28.  * Revision 1.3  1995/07/19  10:04:04  martin
  29.  * first release version, 1.0 beta
  30.  *
  31.  * Revision 1.2  1995/07/04  20:00:33  martin
  32.  * almost completely rewritten, now finally working
  33.  *
  34.  * Revision 1.1  92/12/22  17:01:47  martin
  35.  * Initial revision
  36.  * 
  37.  *
  38.  * 
  39.  */
  40.  
  41. #include <exec/memory.h>
  42. #include <exec/lists.h>
  43. #include <exec/errors.h>
  44. #include <dos/dos.h>
  45. #include <dos/dosextens.h>
  46. #include <dos/filehandler.h>
  47. #include <devices/audio.h>
  48. /* Hack, because registerized BeginIO() doesn't seem to exist in small.lib */
  49. #define BeginIO __stdargs BeginIO
  50. #include <proto/exec.h>
  51. #undef BeginIO
  52.  
  53. #include "audio.h"
  54.  
  55. /* debugging on the serial port */
  56. #define DEBUG
  57. #define DPRINTF KPrintF
  58.  
  59. #define QTOUPPER(c)      ((c)>='a'&&(c)<='z'?(c)-'a'+'A':(c))
  60.  
  61. #undef  BADDR
  62. #define BADDR(x)        ((CPTR)((LONG)x << 2))
  63.  
  64. /*
  65.  * global vars
  66.  * 
  67.  * since several invocations (processes) of the handler use the SAME
  68.  * global data segment, process specific data MUST be allocated or
  69.  * created on the stack. ExecBase can safely be global, though.
  70.  */
  71. extern struct ExecBase *__far AbsExecBase;
  72. struct ExecBase *SysBase;
  73.  
  74. /* Prototypes */
  75. BOOL checkReadyQueue(struct AudioState *as);
  76. BOOL checkIOBHandling(struct AudioState *as);
  77. void swapPtrs(struct AudioState *as);
  78. void allocIO(struct IOAudio *iob, struct AudioOptions *ao);
  79. void freeIO(struct IOAudio *iob, struct AudioOptions *ao);
  80. void lockIO(struct IOAudio *iob, struct AudioOptions *ao);
  81. void abortIO(struct IOAudio *iob, struct AudioOptions *ao);
  82. void writeIO(struct IOAudio *iob, struct AudioBuffer *buffer, struct AudioOptions *ao);
  83. void returnpkt(struct DosPacket *packet, LONG res1, LONG res2, struct AudioOptions *ao);
  84. int strncmpi(char *str1, char *str2, int n);
  85. BOOL parseArgs(struct AudioOptions *ao, int len, char *str);
  86. LONG getLong(int *len, char **ptr);
  87.  
  88. /*
  89.  * quark()
  90.  *
  91.  * entry point for the handler.
  92.  */
  93. void __saveds quark(void)
  94. {
  95.     UBYTE *openstring;            /* BCPL String to Handler Device Name */
  96.     BOOL error = 1;
  97.  
  98.     struct AudioState state;    /* This struct contains the complete information about Audio IO */
  99.  
  100.     struct DeviceNode *devnode;    /* our device node is passed in parm pkt Arg3 */
  101.     struct DosPacket *packet;    /* temporary packet handle, as received from the calling process */
  102.     struct MsgPort *devport;    /* MessagePort of handler process */
  103.     struct Process *process;    /* Process ID of handler process */
  104.     struct Message *message;    /* temporary message handle */
  105.  
  106.     /* First, initialize SysBase, so we may call exec functions */
  107.     SysBase = AbsExecBase;
  108.  
  109.     process = (struct Process *)FindTask(NULL);
  110.     devport = &process->pr_MsgPort;
  111.  
  112.     WaitPort(devport);        /* Wait for startup message */
  113.     message = (struct Message *)GetMsg(devport);
  114.     packet = (struct DosPacket *) message->mn_Node.ln_Name;
  115.  
  116.     openstring = (UBYTE *)BADDR(packet->dp_Arg1);
  117.     devnode = (struct DeviceNode *)BADDR(packet->dp_Arg3);
  118.  
  119.     /* Set default options, currently any channel */
  120.     state.AS_Options.AO_Priority = 0;
  121.     state.AS_Options.AO_NumChannels = 4;
  122.     state.AS_Options.AO_Channels = "\x1\x2\x4\x8";
  123.     state.AS_Options.AO_BufferSize = AUDIOBUFSIZE;
  124.     state.AS_Options.AO_Period = DEFAULT_PERIOD;
  125.     state.AS_Options.AO_Volume = DEFAULT_VOLUME;
  126.     state.AS_Options.AO_Debug = 0;
  127.  
  128.     /* Seek for ':' and interpret everything after as options */
  129.     parseArgs(&(state.AS_Options), openstring[0], openstring+1);
  130.  
  131. #ifdef DEBUG
  132.     if (state.AS_Options.AO_Debug)
  133.         DPRINTF("S%ld", state.AS_Options.AO_Debug);
  134. #endif
  135.  
  136.     /* 
  137.      * create several IO blocks, Messageport and open the device
  138.      * we dont create another port, but use our own port 
  139.      */
  140.     state.AS_IO1 = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
  141.     if (!state.AS_IO1)
  142.         goto handler_exit;
  143.     state.AS_IO2 = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
  144.     if (!state.AS_IO2)
  145.         goto handler_exit;
  146.     state.AS_AllocIO = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
  147.     if (!state.AS_AllocIO)
  148.         goto handler_exit;
  149.     state.AS_LockIO = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
  150.     if (!state.AS_LockIO)
  151.         goto handler_exit;
  152.  
  153.     state.AS_CurrentIO = state.AS_IO1;
  154.     state.AS_NextIO = state.AS_IO2;
  155.  
  156.     state.AS_AllocIO->ioa_Request.io_Message.mn_ReplyPort = devport;
  157.  
  158.     /* link a dummy packet */
  159.     state.AS_DummyPacket.dp_Type = ACTION_WRITE_RETURN;
  160.     state.AS_AllocIO->ioa_Request.io_Message.mn_Node.ln_Name = (UBYTE *)&(state.AS_DummyPacket);
  161.  
  162.     state.AS_BytesCopied = 0;    /* A counter for bytes copied from DOS packet to Audio buffer */
  163.  
  164.     /* allocate chip mem for audio double buffer, initialize all buffer variables */
  165.     state.AS_Buffer1.AB_Data = AllocMem(state.AS_Options.AO_BufferSize, MEMF_CHIP);
  166.     state.AS_Buffer1.AB_Size = state.AS_Options.AO_BufferSize;
  167.     state.AS_Buffer1.AB_End = 0;
  168.     state.AS_Buffer1.AB_InUse = 0;
  169.     if (!state.AS_Buffer1.AB_Data)
  170.         goto handler_exit;
  171.     state.AS_Buffer2.AB_Data = AllocMem(state.AS_Options.AO_BufferSize, MEMF_CHIP);
  172.     state.AS_Buffer2.AB_Size = state.AS_Options.AO_BufferSize;
  173.     state.AS_Buffer2.AB_End = 0;
  174.     state.AS_Buffer2.AB_InUse = 0;
  175.     if (!state.AS_Buffer2.AB_Data)
  176.         goto handler_exit;
  177.  
  178.     /* Current buffer is buffer currently played */
  179.     /* Next buffer is buffer currently being filled with sound data */
  180.     state.AS_CurrentBuffer = &(state.AS_Buffer1);
  181.     state.AS_NextBuffer = &(state.AS_Buffer2);
  182.  
  183.     /* Initialize the DOS packet input queue */
  184.     NewList(&(state.AS_ReadyQueue));
  185.  
  186.     /* Open audio.device, DON'T allocate any channels yet */
  187.     if (error = OpenDevice(AUDIONAME, 0, (struct IORequest *)state.AS_AllocIO, 0)) {
  188.         returnpkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE, &(state.AS_Options));
  189.         goto handler_exit;
  190.     }
  191.  
  192.     state.AS_AllocState = ALLOCSTATE_UNUSED;
  193.     state.AS_LockState = LOCKSTATE_UNUSED;
  194.     state.AS_OpenForOutput = FALSE;
  195.  
  196.     /* devnode->dn_Task = devport;  nope, start one task for each invocation */
  197.  
  198.     /* Finished with startup parameter packet...send back...*/
  199.     returnpkt(packet, DOSTRUE, packet->dp_Res2, &(state.AS_Options));
  200.  
  201.     /*
  202.      * The Main Event Loop:
  203.      *
  204.      * We have the following incoming messages:
  205.      *
  206.      * ACTION_FINDOUTPUT  user process opens our file handle for output
  207.      * ACTION_WRITE       play sound request coming from the user process
  208.      * ACTION_END         the file handle is closed -> we will be closed
  209.      * ACTION_IS_FILESYSTEM is the handler a filesystem? NO
  210.      * return from ADCMD_ALLOCATE  coming from the audio.device
  211.      * return from CMD_WRITE       coming from the audio.device
  212.      * return from ADCMD_FREE      coming from the audio.device
  213.      * the last three will be labeled with ACTION_WRITE_RETURN on our message port.
  214.      * Therefore, a dummy packet is linked with the IO-Requests.
  215.      *
  216.      * ADCMD_ALLOCATE might block if a channel is not available
  217.      * ACTION_WRITE may block, if the buffer is full
  218.      * ACTION_END may block, if there is still sound playing
  219.      *
  220.      * Audio return messages have a dummy dos packet in their ln_Name field,
  221.      * which has a type of ACTION_WRITE_RETURN.
  222.      */
  223.  
  224.     do { 
  225.         WaitPort(devport);
  226.         /* evaluate incoming packets */
  227.         while ((message = (struct Message *) GetMsg(devport)) != 0) {
  228.             packet = (struct DosPacket *) message->mn_Node.ln_Name;
  229.             switch (packet->dp_Type) {
  230.  
  231.                 case ACTION_FINDOUTPUT:
  232.                     /* The device is opened here. We try to allocate requested channel(s) */
  233.                     state.AS_FileHandle = (struct FileHandle *) BADDR(packet->dp_Arg1);
  234. #ifdef DEBUG
  235.                     if (state.AS_Options.AO_Debug)
  236.                         DPRINTF("F%ld", state.AS_Options.AO_Debug);
  237. #endif
  238.                     if (state.AS_OpenForOutput) {
  239.                         returnpkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE, &(state.AS_Options));
  240.                     }
  241.                     else {
  242.                         state.AS_OpenForOutput = DOSTRUE;
  243.                         state.AS_FileHandle->fh_Port = (struct MsgPort *)DOSTRUE;    /* Interactive */
  244.                         state.AS_FileHandle->fh_Arg1 = 42;    /* handler-private info */
  245.                         returnpkt(packet, DOSTRUE, 0L, &(state.AS_Options));
  246.                     }
  247.                     break;
  248.  
  249.                 case ACTION_END:          /* Close request */
  250.                     state.AS_OpenForOutput = FALSE;
  251. #ifdef DEBUG
  252.                     if (state.AS_Options.AO_Debug)
  253.                         DPRINTF("E%ld", state.AS_Options.AO_Debug); 
  254. #endif
  255.                     returnpkt(packet, DOSTRUE, 0L, &(state.AS_Options));
  256.                     break;
  257.  
  258.                 case ACTION_WRITE:                /* Write Request */
  259.                     /* put new write request in the waiting queue */
  260.                     AddTail(&(state.AS_ReadyQueue), (struct Node *)message);
  261. #ifdef DEBUG
  262.                     if (state.AS_Options.AO_Debug)
  263.                         DPRINTF("W%ld", state.AS_Options.AO_Debug);
  264. #endif
  265.                     if (state.AS_AllocState == ALLOCSTATE_UNUSED) {
  266.                         /* allocate channel(s) */
  267.                         /* request new key */
  268.                         state.AS_AllocIO->ioa_AllocKey = 0;
  269.                         allocIO(state.AS_AllocIO, &(state.AS_Options));
  270.                         state.AS_AllocState = ALLOCSTATE_ALLOCATING;
  271.                     }
  272.                     break;
  273.  
  274.                 case ACTION_IS_FILESYSTEM:        /* Is handler a filesystem? */
  275.                     /* It isn't! */
  276. #ifdef DEBUG
  277.                     if (state.AS_Options.AO_Debug)
  278.                         DPRINTF("I%ld", state.AS_Options.AO_Debug);
  279. #endif
  280.                     returnpkt(packet, DOSFALSE, 0L, &(state.AS_Options));
  281.                     break;
  282.  
  283.                 case ACTION_WRITE_RETURN:
  284.                     /* IO Request came back - check which one */
  285.                     switch (((struct IORequest *)message)->io_Command) {
  286.                         case ADCMD_ALLOCATE:
  287.                             /* allocation has finished, initialize iobs with key */
  288.                             *state.AS_IO1 = *state.AS_AllocIO;
  289.                             *state.AS_IO2 = *state.AS_AllocIO;
  290.                             *state.AS_LockIO = *state.AS_AllocIO;
  291.                             state.AS_AllocState = ALLOCSTATE_ALLOCATED;
  292. #ifdef DEBUG
  293.                             if (state.AS_Options.AO_Debug)
  294.                                 DPRINTF("ra%ld", state.AS_Options.AO_Debug);
  295. #endif
  296.                             /* lock the allocated channel */
  297.                             lockIO(state.AS_LockIO, &(state.AS_Options));
  298.                             state.AS_LockState = LOCKSTATE_LOCKED;
  299.                             break;
  300.                         case CMD_WRITE:
  301.                             /* check, which buffer is free again */
  302.                             if ((struct IOAudio *)message == state.AS_IO1) {
  303.                                 state.AS_Buffer1.AB_InUse = 0;
  304.                                 if (state.AS_IO1->ioa_Request.io_Error != IOERR_ABORTED)
  305.                                     state.AS_Buffer1.AB_End = 0;
  306.                             }
  307.                             else {
  308.                                 state.AS_Buffer2.AB_InUse = 0;
  309.                                 if (state.AS_IO2->ioa_Request.io_Error != IOERR_ABORTED)
  310.                                     state.AS_Buffer2.AB_End = 0;
  311.                             }
  312. #ifdef DEBUG
  313.                             if (state.AS_Options.AO_Debug)
  314.                                 DPRINTF("rw%ld", state.AS_Options.AO_Debug);
  315. #endif
  316.                             break;
  317.                         case ADCMD_LOCK:
  318.                             /* LOCK replied */
  319. #ifdef DEBUG
  320.                             if (state.AS_Options.AO_Debug)
  321.                                 DPRINTF("rl%ld", state.AS_Options.AO_Debug);
  322. #endif
  323.                             if (state.AS_LockIO->ioa_Request.io_Error == ADIOERR_CHANNELSTOLEN) {
  324.                                 /* Channel stolen, set allocate flag */
  325.                                 state.AS_AllocState = ALLOCSTATE_STOLEN;
  326.                                 /*
  327.                                  * If NextBuffer is in use, it is currently played,
  328.                                  * and CurrentBuffer is sent, but not yet played by the
  329.                                  * audio.device, so it is safe to AbortIO it without
  330.                                  * losing sound. Anyway, we wait until both io requests
  331.                                  * are inactive before we send the ADCMD_FREE request.
  332.                                  */
  333.                                 if (state.AS_NextBuffer->AB_InUse)
  334.                                     abortIO(state.AS_CurrentIO, &(state.AS_Options));
  335.                                 /* 
  336.                                  * swap, so Current becomes Next to be sent as next after
  337.                                  * channel reallocation.
  338.                                  */
  339.                                 swapPtrs(&state);
  340.                             }
  341.                             state.AS_LockState = LOCKSTATE_UNUSED;
  342.                             break;
  343.                         case ADCMD_FREE:
  344.                             /* Allocation has ended, set state */
  345.                             state.AS_AllocState = ALLOCSTATE_UNUSED;
  346. #ifdef DEBUG
  347.                             if (state.AS_Options.AO_Debug)
  348.                                 DPRINTF("rf%ld", state.AS_Options.AO_Debug);
  349. #endif
  350.                             /* Check if we still have data to send, if yes, the FREE */
  351.                             /* was a response to free stolen channels */
  352.                             if (!IsListEmpty(&(state.AS_ReadyQueue))
  353.                                     || state.AS_Buffer1.AB_End || state.AS_Buffer2.AB_End) {
  354.                                 /* try to allocate the channels again */
  355.                                 /* use old key */
  356.                                 allocIO(state.AS_AllocIO, &(state.AS_Options));
  357.                                 state.AS_AllocState = ALLOCSTATE_ALLOCATING;
  358.                             }
  359.                             break;
  360.                     }
  361.                     break;
  362.                 default:
  363. #ifdef DEBUG
  364.                     if (state.AS_Options.AO_Debug)
  365.                         DPRINTF("P%ld?%ld", packet->dp_Type, state.AS_Options.AO_Debug);
  366. #endif
  367.                     returnpkt(packet, DOSFALSE, ERROR_ACTION_NOT_KNOWN, &(state.AS_Options));
  368.                     break;
  369.             }
  370.         }
  371.             
  372.         /* If there is stealing and audio channel is no longer used, free it */
  373.         if (state.AS_AllocState == ALLOCSTATE_STOLEN
  374.                 && !state.AS_Buffer1.AB_InUse && !state.AS_Buffer2.AB_InUse) {
  375.             /* Free Channel */
  376.             freeIO(state.AS_AllocIO, &(state.AS_Options));
  377.             state.AS_AllocState = ALLOCSTATE_FREEING;
  378.         }
  379.  
  380.         /* As long as there is activity, try handling input and output */
  381.         while (checkReadyQueue(&state) || checkIOBHandling(&state))
  382.             ;
  383.  
  384.         /* Is device being closed? */
  385.         if (!state.AS_OpenForOutput && IsListEmpty(&(state.AS_ReadyQueue))
  386.                 && state.AS_AllocState == ALLOCSTATE_ALLOCATED
  387.                 && !state.AS_Buffer1.AB_InUse && !state.AS_Buffer2.AB_InUse
  388.                 && !state.AS_Buffer1.AB_End && !state.AS_Buffer2.AB_End) {
  389.             /* Free Channel */
  390.             freeIO(state.AS_AllocIO, &(state.AS_Options));
  391.             state.AS_AllocState = ALLOCSTATE_FREEING;
  392.         }
  393.     } while (state.AS_OpenForOutput || !IsListEmpty(&(state.AS_ReadyQueue))
  394.             || state.AS_AllocState != ALLOCSTATE_UNUSED
  395.             || state.AS_LockState != LOCKSTATE_UNUSED);
  396.  
  397. handler_exit:
  398.     if (!error)
  399.         CloseDevice((struct IORequest *)state.AS_AllocIO);
  400.     if (state.AS_IO1)
  401.         FreeMem(state.AS_IO1, sizeof(struct IOAudio));
  402.     if (state.AS_IO2)
  403.         FreeMem(state.AS_IO2, sizeof(struct IOAudio));
  404.     if (state.AS_AllocIO)
  405.         FreeMem(state.AS_AllocIO, sizeof(struct IOAudio));
  406.     if (state.AS_LockIO)
  407.         FreeMem(state.AS_LockIO, sizeof(struct IOAudio));
  408.     if (state.AS_Buffer1.AB_Data)
  409.         FreeMem(state.AS_Buffer1.AB_Data, state.AS_Buffer1.AB_Size);
  410.     if (state.AS_Buffer2.AB_Data)
  411.         FreeMem(state.AS_Buffer2.AB_Data, state.AS_Buffer2.AB_Size);
  412.  
  413.     /* end of main handler code */
  414. #ifdef DEBUG
  415.     if (state.AS_Options.AO_Debug)
  416.         DPRINTF("X%ld\n", state.AS_Options.AO_Debug);
  417. #endif
  418. }
  419. /*
  420.  * checkReadyQueue()
  421.  *
  422.  * check, if there are still incoming packets not already handled
  423.  */
  424. BOOL checkReadyQueue(struct AudioState *as)
  425. {
  426.     struct Message *msg = (struct Message *)as->AS_ReadyQueue.lh_Head;
  427.     BOOL active = FALSE;
  428.  
  429.     if (!as->AS_NextBuffer->AB_InUse && msg->mn_Node.ln_Succ) {
  430.         struct DosPacket *p = (struct DosPacket *) msg->mn_Node.ln_Name;
  431.         UBYTE *src, *dst;
  432.         LONG restlen, packetlen;
  433.  
  434.         /* try to fit write request into current 'new'ábuffer */
  435.         restlen = as->AS_NextBuffer->AB_Size - as->AS_NextBuffer->AB_End;
  436.         if (restlen) {    /* Buffer is not yet full */
  437.             packetlen = p->dp_Arg3 - as->AS_BytesCopied;
  438.             src = (UBYTE *)p->dp_Arg2 + as->AS_BytesCopied;
  439.             dst = as->AS_NextBuffer->AB_Data + as->AS_NextBuffer->AB_End;
  440.             if (packetlen <= restlen) {
  441.                 /* copy whole packet and reply */
  442.                 LONG i;
  443.                 for (i = packetlen; i; --i)
  444.                     *dst++ = *src++;
  445.                 Remove(&(msg->mn_Node));
  446.                 returnpkt(p, p->dp_Arg3, 0, &(as->AS_Options));
  447.                 as->AS_BytesCopied = 0;
  448.                 as->AS_NextBuffer->AB_End += packetlen;
  449.             }
  450.             else {
  451.                 /* Buffer is full */
  452.                 LONG i;
  453.                 for (i = restlen; i; --i)
  454.                     *dst++ = *src++;
  455.                 as->AS_BytesCopied += restlen;
  456.                 as->AS_NextBuffer->AB_End = as->AS_NextBuffer->AB_Size;
  457.             }
  458.             active = TRUE;
  459.         }
  460.     }
  461.     return active;
  462. }
  463.  
  464.  
  465. /*
  466.  * checkIOBHandling()
  467.  *
  468.  * check if a buffer can be sent to the audio.device
  469.  */
  470. BOOL checkIOBHandling(struct AudioState *as)
  471. {
  472.     BOOL active = FALSE;
  473.  
  474.     if (as->AS_AllocState == ALLOCSTATE_ALLOCATED) {
  475.         /* check if NextBuffer can be sent to the audio.device, either */
  476.         /* if the buffer is full, or the ReadyQueue is empty and the device is closing */
  477.         if (!as->AS_NextBuffer->AB_InUse 
  478.                 && (as->AS_NextBuffer->AB_End == as->AS_NextBuffer->AB_Size
  479.                     || as->AS_NextBuffer->AB_End > 0 && (!as->AS_OpenForOutput) 
  480.                         && IsListEmpty(&(as->AS_ReadyQueue)))) {
  481.             /* send the buffer off */
  482.             writeIO(as->AS_NextIO, as->AS_NextBuffer, &(as->AS_Options));
  483.             as->AS_NextBuffer->AB_InUse = 1;
  484.             /* swap buffers */
  485.             swapPtrs(as);
  486.             active = TRUE;
  487.         }
  488.     }
  489.     return active;
  490. }
  491.  
  492. /*
  493.  * swapPtrs()
  494.  *
  495.  * swap two pointers
  496.  */
  497. void swapPtrs(struct AudioState *as)
  498. {
  499.     struct AudioBuffer *tmpbuf;
  500.     struct IOAudio *tmpio;
  501.  
  502.     tmpbuf = as->AS_NextBuffer;
  503.     as->AS_NextBuffer = as->AS_CurrentBuffer;
  504.     as->AS_CurrentBuffer = tmpbuf;
  505.     tmpio = as->AS_NextIO;
  506.     as->AS_NextIO = as->AS_CurrentIO;
  507.     as->AS_CurrentIO = tmpio;
  508. }
  509.  
  510. /*
  511.  * allocIO()
  512.  *
  513.  * allocate one or more audio channels.
  514.  */
  515. void allocIO(struct IOAudio *iob, struct AudioOptions *ao)
  516. {
  517.     iob->ioa_Request.io_Message.mn_Node.ln_Pri = ao->AO_Priority;
  518.     iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
  519.     iob->ioa_Request.io_Flags = 0;
  520.     iob->ioa_Data = ao->AO_Channels;
  521.     iob->ioa_Length = ao->AO_NumChannels;
  522.     BeginIO((struct IORequest *)iob);
  523. #ifdef DEBUG
  524.     if (ao->AO_Debug)
  525.         DPRINTF("a%ld", ao->AO_Debug);
  526. #endif
  527. }
  528.  
  529. /*
  530.  * lockIO()
  531.  *
  532.  * lock one or more audio channels.
  533.  */
  534. void lockIO(struct IOAudio *iob, struct AudioOptions *ao)
  535. {
  536.     iob->ioa_Request.io_Command = ADCMD_LOCK;
  537.     iob->ioa_Request.io_Flags = 0;
  538.     BeginIO((struct IORequest *)iob);
  539. #ifdef DEBUG
  540.     if (ao->AO_Debug)
  541.         DPRINTF("l%ld", ao->AO_Debug);
  542. #endif
  543. }
  544.  
  545. /*
  546.  * freeIO()
  547.  *
  548.  * allocate one or more audio channels.
  549.  */
  550. void freeIO(struct IOAudio *iob, struct AudioOptions *ao)
  551. {
  552.     iob->ioa_Request.io_Command = ADCMD_FREE;
  553.     iob->ioa_Request.io_Flags = 0;
  554.     BeginIO((struct IORequest *)iob);
  555. #ifdef DEBUG
  556.     if (ao->AO_Debug)
  557.         DPRINTF("f%ld", ao->AO_Debug);
  558. #endif
  559. }
  560.  
  561. /*
  562.  * writeIO()
  563.  *
  564.  * allocate one or more audio channels.
  565.  */
  566. void writeIO(struct IOAudio *iob, struct AudioBuffer *buffer, struct AudioOptions *ao)
  567. {
  568.     iob->ioa_Request.io_Message.mn_Node.ln_Pri = ao->AO_Priority;
  569.     iob->ioa_Request.io_Command = CMD_WRITE;
  570.     iob->ioa_Request.io_Flags = ADIOF_PERVOL;
  571.     iob->ioa_Data = buffer->AB_Data;
  572.     iob->ioa_Length = buffer->AB_End;
  573.     iob->ioa_Period = ao->AO_Period;
  574.     iob->ioa_Volume = ao->AO_Volume;
  575.     iob->ioa_Cycles = 1;
  576.     BeginIO((struct IORequest *)iob);
  577. #ifdef DEBUG
  578.     if (ao->AO_Debug)
  579.         DPRINTF("w%ld", ao->AO_Debug);
  580. #endif
  581. }
  582.  
  583. /*
  584.  * abortIO()
  585.  *
  586.  * Abort a CMD_WRITE request
  587.  */
  588. void abortIO(struct IOAudio *iob, struct AudioOptions *ao)
  589. {
  590. #ifdef DEBUG
  591.     if (ao->AO_Debug)
  592.         DPRINTF("bw%ld", ao->AO_Debug);
  593. #endif
  594.     AbortIO((struct IORequest *)iob);
  595. }
  596.  
  597. /*
  598.  * returnpkt()
  599.  *
  600.  * return a packet to calling DOS process.
  601.  */
  602. void returnpkt(struct DosPacket *packet, LONG res1, LONG res2, struct AudioOptions *ao)
  603. {
  604.     struct Message *message = packet->dp_Link;
  605.     struct MsgPort *replyport = packet->dp_Port;
  606.     struct Process *process = (struct Process *)FindTask(NULL);
  607.  
  608.     packet->dp_Res1 = res1;
  609.     packet->dp_Res2 = res2; 
  610.     packet->dp_Port = &(process->pr_MsgPort);
  611.     message->mn_Node.ln_Name = (char *) packet;
  612.  
  613. #ifdef DEBUG
  614.     if (ao->AO_Debug)
  615.         DPRINTF("RP%ld", ao->AO_Debug);
  616. #endif
  617.     PutMsg(replyport,message); 
  618. }
  619.  
  620. /*
  621.  * parseArgs()
  622.  *
  623.  * parse arguments of the device string (after the colon ':')
  624.  */
  625. BOOL parseArgs(struct AudioOptions *ao, int len, char *str)
  626. {
  627.     LONG val;
  628.     while (len && *str != ':') {
  629.         len--, str++;
  630.     }
  631.     if (*str != ':')
  632.         return FALSE;
  633.     len--, str++;
  634.     while (len > 0) {
  635.         if (!strncmpi(str, "PERIOD", 6)) {
  636.             len -= 6, str += 6;
  637.             val = getLong(&len, &str);
  638.             if (val >= 124 && val <= 65535)
  639.                 ao->AO_Period = val;
  640.         }
  641.         else if (!strncmpi(str, "FREQUENCY", 9)) {
  642.             len -= 9, str += 9;
  643.             val = getLong(&len, &str);
  644.             if (val >= 55 && val <= 28603)
  645.                 ao->AO_Period = CLOCK_CONSTANT_PAL / val;
  646.         }
  647.         else if (!strncmpi(str, "VOLUME", 6)) {
  648.             len -= 6, str += 6;
  649.             val = getLong(&len, &str);
  650.             if (val >= 0 && val <= 64)
  651.                 ao->AO_Volume = val;
  652.         }
  653.         else if (!strncmpi(str, "PRIORITY", 8)) {
  654.             len -= 8, str += 8;
  655.             val = getLong(&len, &str);
  656.             if (val >= -128 && val <= 127)
  657.                 ao->AO_Priority = val;
  658.         }
  659.         else if (!strncmpi(str, "BUFFER", 6)) {
  660.             len -= 6, str += 6;
  661.             val = getLong(&len, &str);
  662.             if (val >= 0x1000 && val <= 0x40000) {
  663.                 val = val / 4;                        /* 2 buffers */
  664.                 ao->AO_BufferSize = val * 2;    /* must be even size */
  665.             }
  666.         }
  667.         else if (!strncmpi(str, "CHANNEL", 7)) {
  668.             len -= 7, str += 7;
  669.             val = getLong(&len, &str);
  670.             if (val >= 0 && val <= 3) {
  671.                 ao->AO_NumChannels = 1;
  672.                 ao->AO_Channels = "\x1\x2\x4\x8" + val;
  673.             }
  674.         }
  675.         else if (!strncmpi(str, "LEFT", 4)) {
  676.             len -= 4, str += 4;
  677.             ao->AO_NumChannels = 2;
  678.             ao->AO_Channels = "\x1\x8";
  679.         }
  680.         else if (!strncmpi(str, "RIGHT", 5)) {
  681.             len -= 5, str += 5;
  682.             ao->AO_NumChannels = 2;
  683.             ao->AO_Channels = "\x2\x4";
  684.         }
  685.         else if (!strncmpi(str, "STEREO", 6)) {
  686.             len -= 6, str += 6;
  687.             ao->AO_NumChannels = 4;
  688.             ao->AO_Channels = "\x3\x5\xA\xC";
  689.             /* but not yet fully implemented! */
  690.         }
  691.         else if (!strncmpi(str, "16BIT", 5)) {
  692.             len -= 5, str += 5;
  693.             /* not implemented! */
  694.         }
  695.         else if (!strncmpi(str, "DEBUG", 5)) {
  696.             len -= 5, str += 5;
  697.             val = getLong(&len, &str);
  698.             ao->AO_Debug = val;
  699.         }
  700.         while (len && *str != '/') /* skip rest (as CON: does, too) */
  701.             len--, str++;
  702.         if (*str == '/')
  703.             len--, str++;
  704.     }
  705.     return TRUE;
  706. }
  707.  
  708. /*
  709.  * getLong()
  710.  *
  711.  * convert string to integer, supports decimal, octal and hex
  712.  */
  713. LONG getLong(int *len, char **ptr)
  714. {
  715.     LONG val = 0;
  716.     int l = *len;
  717.     char *str = *ptr;
  718.     int sign = 1;
  719.  
  720.     if (l == 0)
  721.         return val;
  722.  
  723.     while (*str == ' ' || *str == '\t')
  724.         str++;
  725.  
  726.     if (*str == '-') {
  727.         sign = -1;
  728.         str++;
  729.     }
  730.     if (*str == '0') {
  731.         l--, str++;
  732.         if (l == 0) {
  733.             *len = l, *ptr = str;
  734.             return val;
  735.         }
  736.         if (QTOUPPER(*str) == 'X') {
  737.             l--, str++;
  738.             while (len > 0) {
  739.                 if (*str >= '0' && *str <= '9')
  740.                     val = val * 16 + *str - '0';
  741.                 else if (QTOUPPER(*str) >= 'A' && QTOUPPER(*str) <= 'F')
  742.                     val = val * 16 + QTOUPPER(*str) - 'A';
  743.                 else
  744.                     break;
  745.                 l--, str++;
  746.             }
  747.         }
  748.         else {
  749.             while (len > 0 && *str >= '0' && *str <= '7') {
  750.                 val = val * 8 + *str - '0';
  751.                 l--, str++;
  752.             }
  753.         }
  754.     }
  755.     else {
  756.         while (len > 0 && *str >= '0' && *str <= '9') {
  757.             val = val * 10 + *str - '0';
  758.             l--, str++;
  759.         }
  760.     }
  761.     *len = l, *ptr = str;
  762.  
  763.     return val * sign;
  764. }
  765.  
  766. /*
  767.  * strncmpi()
  768.  *
  769.  * compare two strings, length n, ignorecase
  770.  */
  771. int strncmpi(char *str1, char *str2, int n)
  772. {
  773.     UBYTE *astr = str1;
  774.     UBYTE *bstr = str2;
  775.     UBYTE c;
  776.  
  777.     while ((c = QTOUPPER(*astr)) && (c == QTOUPPER(*bstr)) && n) 
  778.         astr++, bstr++, n--;
  779.     if (!c || !n)
  780.         return 0;
  781.     if (c < *bstr)
  782.         return -1;
  783.     return 1;
  784. }
  785.